﻿using System;
using System.Xml.Serialization;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Xml;
using Vbyte.Extension;

namespace Vbyte.Configuration
{
    /// <summary>
    /// 参数构建器
    /// </summary>
    [Serializable]
    public class ParamsBuild
    {
        /// <summary>
        /// 获取或设置方法参数配置集合
        /// </summary>
        /// <value>The param config.</value>
        [XmlElement(ElementName = "Config")]
        public MethodParams[] ParamConfig { get; set; }

        /// <summary>
        /// 获取指定类型名称的方法参数
        /// </summary>
        /// <param name="typeFullName">类型全称</param>
        /// <param name="methodName">方法名称</param>
        /// <returns></returns>
        public MethodParams GetParamConfig(string typeFullName, string methodName)
        {
            MethodParams result = null;
            foreach (MethodParams pm in ParamConfig)
            {
                if (pm.TypeFullName == typeFullName && pm.MethodName == methodName)
                {
                    result = pm;
                    break;
                }
            }
            return result;
        }

        /// <summary>
        /// 获取当前调用方法的方法参数配置
        /// </summary>
        /// <returns></returns>
        public MethodParams GetParamConfig()
        {
            MethodBase method = new StackFrame(1).GetMethod();
            return GetParamConfig(method.DeclaringType.FullName, method.Name);
        }

        /// <summary>
        /// 获取同类型中其他方法名的参数配置
        /// </summary>
        /// <param name="methodName">方法名称</param>
        /// <returns></returns>
        public MethodParams GetParamConfig(string methodName)
        {
            MethodBase method = new StackFrame(1).GetMethod();
            return GetParamConfig(method.DeclaringType.FullName, methodName);
        }

        private static ParamsBuild _instance;
        /// <summary>
        /// 获取配置文件中的实例
        /// </summary>
        public static ParamsBuild ConfigInstance
        {
            get 
            {
                if (_instance == null)
                { 
                    _instance = XmlSerializeSectionHandler.GetObject<ParamsBuild>("ParamsBuild");
                }
                return _instance;
            }
        }
    }

    /// <summary>
    /// 方法参数配置
    /// </summary>
    [Serializable]
    public class MethodParams : ICloneable
    {
        /// <summary>
        /// 类型全称
        /// </summary>
        [XmlAttribute(AttributeName="type")]
        public string TypeFullName { get; set; }

        /// <summary>
        /// 方法名
        /// </summary>
        [XmlAttribute(AttributeName = "method")]
        public string MethodName { get; set; }

        /// <summary>
        /// 参数集合
        /// </summary>
        public Param[] Params { get; set; }

        /// <summary>
        /// 获取参数集合中特定名称的参数
        /// </summary>
        /// <param name="paramName">参数名称</param>
        /// <returns></returns>
        public Param GetParam(string paramName)
        {
            Param p = null;
            if (Params != null && Params.Length > 0)
            {
                foreach (Param pi in Params)
                {
                    if (pi.Name == paramName)
                    {
                        p = pi;
                        break;
                    }
                }
            }
            return p;
        }

        /// <summary>
        /// [穷举]获取所有方法最个可能参数的的情况(各个参数可能情况的乘积)
        /// </summary>
        /// <returns></returns>
        public MethodParams[] GetAvailableMethodParams()
        {
            List<MethodParams> mparmList = new List<MethodParams>();
            bool addDefaultMethodParams = false;
            List<Param> baseParams = new List<Param>();
            foreach (Param pm in Params)
            {
                foreach (Param rpm in Params)
                {
                    if (rpm.Name == pm.Name)
                    {
                        #region 当前参数
                        int totalCount = rpm.GetUsableValueCount();
                        //Console.WriteLine(rpm.Name + " : " + totalCount);
                        if (totalCount == 1)
                        {
                            if (rpm.IsOptional) baseParams.Add(rpm);
                            if (!addDefaultMethodParams)
                            {
                                MethodParams defaultMethodParams = new MethodParams();
                                defaultMethodParams.TypeFullName = TypeFullName;
                                defaultMethodParams.MethodName = MethodName;
                                defaultMethodParams.Params = Params;
                                mparmList.Add(defaultMethodParams);

                                addDefaultMethodParams = true;
                            }
                        }
                        else
                        {
                            #region 穷举参数的所有可能值清况
                            List<Param> rpmAvailableList = new List<Param>();
                            foreach (Param rpmAvailable in Params)
                            {
                                if (rpmAvailable.Name != rpm.Name)
                                {
                                    rpmAvailableList.Add(rpmAvailable);
                                }
                            }
                            rpmAvailableList.Add(null);

                            int t = mparmList.Count;
                            //Console.WriteLine("Available Count:" + t);
                            for (int i = 0; i < totalCount; i++)
                            {

                                Param parmAvailale = (Param)rpm.Clone();
                                parmAvailale.Value = rpm.GetUsableValue(i).ToString();
                                rpmAvailableList[rpmAvailableList.Count - 1] = parmAvailale;

                                //小于一个单位
                                if (t < 1)
                                {
                                    #region 基数为1单位时，加法即乘法。
                                    MethodParams mparmAvailale = new MethodParams();
                                    mparmAvailale.TypeFullName = TypeFullName;
                                    mparmAvailale.MethodName = MethodName;
                                    mparmAvailale.Params = rpmAvailableList.ToArray();
                                    //Console.WriteLine("< 1 Unit,ADD:---" + parmAvailale.Value);
                                    mparmList.Add(mparmAvailale);
                                    #endregion
                                }
                                else
                                {
                                    for (int k = 0; k < t; k++)
                                    {
                                        #region 乘以基数运算
                                        MethodParams mparmInList = mparmList[k];
                                        //Console.WriteLine(k);

                                        bool blnAddNewMethodParam = true;

                                        #region 检查是否有重复的方法参数定义
                                        foreach (Param parminList in mparmInList.Params)
                                        {
                                            if (parminList.Name == rpm.Name)
                                            {
                                                if (parminList.Value == parmAvailale.Value)
                                                {
                                                    blnAddNewMethodParam = false;
                                                    //Console.WriteLine("Not ADD:" + parmAvailale.Value);
                                                    break;
                                                }
                                            }
                                        }
                                        #endregion

                                        if (blnAddNewMethodParam == true)
                                        {
                                            Param[] upataParams = new Param[mparmInList.Params.Length];
                                            #region DeepClone
                                            for (int u = 0; u < upataParams.Length; u++)
                                            {
                                                Param pu = (Param)mparmInList.Params[u].Clone();
                                                upataParams[u] = pu;
                                            } 
                                            #endregion

                                            #region Update
                                            int idx = 0;
                                            foreach (Param parminListUpdate in mparmInList.Params)
                                            {
                                                if (parminListUpdate.Name == rpm.Name)
                                                {
                                                    upataParams[idx].Value = parmAvailale.Value;
                                                    break;
                                                }
                                                idx++;
                                            }
                                            #endregion

                                            MethodParams mparmNew = new MethodParams();
                                            mparmNew.TypeFullName = TypeFullName;
                                            mparmNew.MethodName = MethodName;
                                            mparmNew.Params = upataParams;
                                            //Console.WriteLine("Update:---" + parmAvailale.Value);
                                            mparmList.Add(mparmNew);
                                        }
                                        #endregion
                                    }
                                    //Console.WriteLine("-----" + t + "*" + (i+1).ToString());
                                }
                            }
                            #endregion
                        }

                        if (rpm.IsOptional)
                        {
                            #region 该参数可选情况
                            if (mparmList.Count < 1)
                            {
                                List<Param> rpmOptionList = new List<Param>();
                                foreach (Param rpmOpt in Params)
                                {
                                    if (rpmOpt.Name != rpm.Name)
                                    {
                                        rpmOptionList.Add(rpmOpt);
                                    }
                                }

                                MethodParams mparmOpt = new MethodParams();
                                mparmOpt.TypeFullName = TypeFullName;
                                mparmOpt.MethodName = MethodName;
                                mparmOpt.Params = rpmOptionList.ToArray();
                                mparmList.Add(mparmOpt);
                            }
                            else
                            {
                                #region X2 可选操作
                                for (int ei = 0; ei < mparmList.Count; ei++)
                                {
                                    List<Param> eiOptionList = new List<Param>();
                                    foreach (Param rpmOpt in mparmList[ei].Params)
                                    {
                                        if (rpmOpt.Name != rpm.Name) eiOptionList.Add(rpmOpt);
                                    }

                                    Param[] eiToAddArray = eiOptionList.ToArray();
                                    if (!ExistsMethodParams(mparmList, eiToAddArray))
                                    {
                                        MethodParams eiParmOpt = (MethodParams)mparmList[ei].Clone();
                                        eiParmOpt.Params = eiToAddArray;
                                        mparmList.Add(eiParmOpt);
                                    }
                                }
                                #endregion
                            }
                            #endregion
                        }
                        #endregion
                    }
                }
            }

            if (baseParams.Count == Params.Length)
            {
                MethodParams baseMethodParams = new MethodParams();
                baseMethodParams.TypeFullName = TypeFullName;
                baseMethodParams.MethodName = MethodName;
                baseMethodParams.Params = baseParams.ToArray();
                mparmList.Add(baseMethodParams);
            }
            return mparmList.ToArray();
        }

        /// <summary>
        /// 检查所有的参数集合中是否存在指定的方法参数
        /// </summary>
        private bool ExistsMethodParams(List<MethodParams> plist, Param[] MethodParamsArray)
        {
            bool exists = false;
            for (int ichk = 0; ichk < plist.Count; ichk++)
            {
                Param[] toCHKArray = plist[ichk].Params;

                //参数个数不匹配
                if (toCHKArray.Length != MethodParamsArray.Length)
                {
                    continue;
                }
                else
                {
                    int eqTotal = 0;
                    foreach (Param chkParam in toCHKArray)
                    {
                        for (int eic = 0; eic < MethodParamsArray.Length; eic++)
                        {
                            if (chkParam.Name == MethodParamsArray[eic].Name
                                && chkParam.Value == MethodParamsArray[eic].Value)
                            {
                                eqTotal++;
                            }
                        }
                    }

                    if (eqTotal == MethodParamsArray.Length)
                    {
                        exists = true;
                        break;
                    }
                }

            }
            return exists;
        }

        #region ICloneable 成员

        /// <summary>
        /// 创建作为当前实例副本的新对象。
        /// </summary>
        /// <returns>作为此实例副本的新对象。</returns>
        public object Clone()
        {
            MethodParams m = new MethodParams();
            m.TypeFullName = TypeFullName;
            m.MethodName = MethodName;
            Param[] mParams = new Param[Params.Length];
            for (int i = 0; i < mParams.Length; i++)
            {
                mParams[i] = (Param)Params[i].Clone();
            }
            m.Params = mParams;
            return m;
        }

        #endregion
    }

    /// <summary>
    /// 测试参数
    /// </summary>
    [Serializable]
    public class Param : ICloneable
    {
        /// <summary>
        /// 参数名称
        /// </summary>
        [XmlAttribute(AttributeName = "name")]
        public string Name { get; set; }

        /// <summary>
        /// 参数类型名称
        /// </summary>
        [XmlAttribute(AttributeName = "type")]
        public string ParamType  { get; set; }

        /// <summary>
        /// 参数的值
        /// </summary>
        public string Value { get; set; }

        /// <summary>
        /// 参数默认值
        /// </summary>
        public string Default { get; set; }

        /// <summary>
        /// 是否是可选参数
        /// </summary>
        [XmlAttribute(AttributeName = "optional")]
        public bool IsOptional { get; set; }

        /// <summary>
        /// 其他可使用的测试数据，默认使用英文逗号分隔各个值。
        /// <para>支持区间语法：</para>
        /// <para>1.普通数字区间：1-100[;1] 即1-100的数字，1为步长。</para>
        /// <para>2.普通字母区间：a-z[;1] 即a-z的字符，1为步长，结果为a,b,c,d,e,f...z。</para>
        /// <para>3.带小数的区间：0.00-1.00[;0.01] 即0-1之间的两位小数，0.01为步长。</para>
        /// <para>4.完整语法定义：开始-结束[;步长][填充模式,填充长度(前面至0)]，[]内参数可选，默认步长为1个单位。
        /// 形如：UsableValues = "125.00-124.00;0.01;[url?down-{0}.html,5]"</para>
        /// </summary>
        public string UsableValues { get; set; }

        /// <summary>
        /// 其他可使用值的分隔符
        /// </summary>
        [XmlAttribute(AttributeName = "split")]
        public string UsableValueSplit { get; set; }

        /// <summary>
        /// 获取参数的强类型值
        /// </summary>
        /// <returns></returns>
        public object GetValue()
        {
            if (Value == null)
            {
                if (Default != null) Value = Default;
            }
            object result = Value;
            if (this.ParamType != null)
            {
                //XML反序列化获取参数的值
                Type pType = Type.GetType(ParamType);
                if (pType.HasAttribute(typeof(System.SerializableAttribute), true))
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(Value.Trim());

                    MethodInfo fun = typeof(ReflectExtension).GetMethod("GetObject", new Type[] { typeof(XmlDocument) });
                    fun = fun.MakeGenericMethod(pType);
                    return fun.Invoke(null, new object[] { xmlDoc });
                }
                else
                {
                    result = Convert.ChangeType(result, pType);
                }
            }
            return result;
        }

        /// <summary>
        /// 其他可选参数值集合
        /// </summary>
        private object[] OtherUsableValueCollection = new Object[0];

        private void BuildOtherUsableValues(string otherValueConfig, string split)
        {
            if (string.IsNullOrEmpty(split)) split = ",";
            if (split != "-")
            {
                OtherUsableValueCollection = Regex.Split(otherValueConfig, Regex.Escape(split));
            }
            else
            {
                #region 测试草稿 区间与模式填充
                // (\-?[\d\.a-zA-Z]+)\s?\-\s?(\-?[\d\.a-zA-Z]+)(;(\-?[\d\.]+))?(;\[(.+),(\d+)\])?
                // (\\-?[\\d\\.a-zA-Z]+)\\s?\\-\\s?(\\-?[\\d\\.a-zA-Z]+)(;(\\-?[\\d\\.]+))?(;\\[(.+),(\\d+)\\])?

                /*
                 -398555 - -500000
                 398555-500000
                 A-Z;1;["http://gwsoft.com.cn/{0}.html",1]
                 1.00-9.00;0.05;["a{0}.,html",1]

                1.区间开始 2.区间结束 4.步长,默认为1个单位  6.替换模型 7.匹配位数,不足填0补齐
 
               -------Found MATCH---Length:8----
                (398555-500000)(398555)(500000)()()()()()

                -------Found MATCH---Length:8----
                (A-Z;1;["http://gwsoft.com.cn/{0}.html",1])(A)(Z)(;1)(1)(;["http://gwsoft.com.cn/{0}.html",1])("http://gwsoft.com.cn/{0}.html")(1)

                -------Found MATCH---Length:8----
                (1.00-9.00;0.05;["a{0}.,html",1])(1.00)(9.00)(;0.05)(0.05)(;["a{0}.,html",1])("a{0}.,html")(1)

                 */
                #endregion

                string numPattern = "(\\-?[\\d\\.a-zA-Z]+)\\s?\\-\\s?(\\-?[\\d\\.a-zA-Z]+)(;(\\-?[\\d\\.]+))?(;\\[(.+),(\\d+)\\])?";
                Match m = Regex.Match(otherValueConfig, numPattern);
                if (!m.Success)
                {
                    throw new System.Configuration.ConfigurationErrorsException("对于可用值的区间配置(-)必须指定开始和结束数字或字母");
                }
                else
                {
                    //区间范围
                    string[] rangDat = new string[] { m.Groups[1].Value, m.Groups[2].Value };

                    //默认步长为1个单位,填充位数为1
                    double step = 1.00;
                    int fillLen = 1;
                    bool cusStepSet = false, restore2Char = false;
                    string fillMode = string.Empty;

                    #region 自动构建数字区间
                    if (m.Groups.Count == 8)
                    {
                        //定义了步长 m.Groups[4].Value
                        if (!string.IsNullOrEmpty(m.Groups[4].Value))
                        {
                            step = Convert.ToDouble(m.Groups[4].Value);
                            cusStepSet = true;
                        }

                        //定义了替换模型和匹配补齐规则 m.Groups[6].Value 和 m.Groups[7].Value
                        if (!string.IsNullOrEmpty(m.Groups[6].Value) &&
                            !string.IsNullOrEmpty(m.Groups[7].Value))
                        {
                            fillMode = m.Groups[6].Value;
                            fillLen = int.Parse(m.Groups[7].Value);
                        }
                    }
                    #endregion

                    bool isDouble = false;
                    int numLen = 0;
                    double rangeBegin, rangeEnd;

                    #region 设置循环区间与步长
                    if (Char.IsLetter(rangDat[0], 0))
                    {
                        rangeBegin = Convert.ToDouble((int)rangDat[0][0]);
                        rangeEnd = Convert.ToDouble((int)rangDat[1][0]);
                        restore2Char = true;
                    }
                    else
                    {
                        if (rangDat[0].Contains(".")) isDouble = true;
                        rangeBegin = double.Parse(rangDat[0]);
                        rangeEnd = double.Parse(rangDat[1]);
                    }

                    if (isDouble)
                    {
                        numLen = Regex.Match(rangDat[0], "\\.(\\d+)$").Groups[1].Value.Length;
                        if (!cusStepSet)
                        {
                            step = 1.00 / Math.Pow(10.00, Convert.ToDouble(numLen));
                        }
                    }
                    #endregion

                    //递减
                    if (rangeBegin.CompareTo(rangeEnd) > 0 && step > 0.00) step = 0.00 - step;

                    Func<bool> fFlag = new Func<bool>(delegate() { return rangeBegin <= rangeEnd;});
                    //是否递减
                    if (step < 0.00)
                    {
                        fFlag = new Func<bool>(delegate() { return rangeBegin >= rangeEnd; });
                    }

                    string strTemp = string.Empty;
                    List<string> strValues = new List<string>();
                    while (fFlag())
                    {
                        #region 循环
                        if (restore2Char == true)
                        {
                            if (fillMode != string.Empty)
                            {
                                strTemp = String.Format(fillMode, ((char)((int)rangeBegin)).ToString().PadLeft(fillLen, '0'));
                            }
                            else
                            {
                                strTemp = ((char)((int)rangeBegin)).ToString();
                            }
                        }
                        else
                        {
                            strTemp = (isDouble) ? rangeBegin.ToString("N" + numLen) : rangeBegin.ToString();
                            if (fillMode != string.Empty)
                            {
                              strTemp = String.Format(fillMode, strTemp.PadLeft(fillLen, '0'));
                            }
                        }

                        //Console.WriteLine(strTemp);
                        strValues.Add(strTemp);
                        rangeBegin += step;

                        #endregion
                    }
                    OtherUsableValueCollection = strValues.ToArray();
                }
            }
        }

        /// <summary>
        /// 获取可用参数值的个数
        /// </summary>
        /// <returns></returns>
        public int GetUsableValueCount()
        {
            if (UsableValues == null) return 1;
            if (OtherUsableValueCollection.Length == 0)
            {
                BuildOtherUsableValues(UsableValues, UsableValueSplit ?? ",");
            }

            //其他可用和默认值
            return OtherUsableValueCollection.Length + 1;
        }

        /// <summary>
        /// 获取可用第N个参数
        /// </summary>
        /// <param name="idx">可选的参数索引</param>
        /// <returns></returns>
        public object GetUsableValue(int idx)
        {
            object result = null;
            int totalCount = GetUsableValueCount();
            if (totalCount > 0 && idx < totalCount)
            {
                if (idx == 0)
                {
                    result = GetValue();
                }
                else
                {
                    result = OtherUsableValueCollection[idx - 1];
                    if (this.ParamType != null)
                    {
                        result = Convert.ChangeType(result, Type.GetType(ParamType));
                    }
                }
            }
            return result;
        }

        /// <summary>
        /// 获取当前参数的值定义的字符串表示
        /// </summary>
        public override string ToString()
        {
            return string.Concat("{", string.Format("name:{0},type:{1},Value:{2},optional:{3},Default:{4},UsableValues:{5},split:{6}",
                Name,
                ParamType ?? "string",
                Value,
                IsOptional,
                Default ?? "null",
                UsableValues ?? "null",
                UsableValueSplit ?? "\",\""),
                "}");
        }


        #region ICloneable 成员

        /// <summary>
        /// 创建作为当前实例副本的新对象。
        /// </summary>
        /// <returns>作为此实例副本的新对象。</returns>
        public object Clone()
        {
            Param p = new Param();
            p.Name = Name;
            p.ParamType = ParamType;
            p.Value = Value;
            p.Default = Default;
            p.IsOptional = IsOptional;
            p.UsableValues = UsableValues;
            p.UsableValueSplit = UsableValueSplit;
            return p;
        }

        #endregion
    }

}
